unit Creature;

interface

uses Engine, DGLE, DGLE_Types, Entity, Types, Bar, Digit;

const
  CreatureFName: array [0..CREATURES_COUNT - 1] of AnsiString = (
  'Gnome.png', 'Spider.png'  
  );

function GetDist(x1, y1, x2, y2: single): word;  

type
  TForce = (fcAlly, fcEnemy);

type
  TCreature = class(TEntity)
  private
    FActive: Boolean;
    FLife: TBar;
    FForce: TForce;
    FTarget: TPoint;
    FSpeed: TBar;
    procedure SetActive(const Value: Boolean);
    procedure SetLife(const Value: TBar);
    procedure SetForce(const Value: TForce);
    procedure SetTarget(const Value: TPoint);
    procedure SetSpeed(const Value: TBar);
  public
    constructor Create(X, Y, ID: Integer; Force: TForce);
    destructor Destroy; override;
    property Active: Boolean read FActive write SetActive;
    property Life: TBar read FLife write SetLife;
    property Speed: TBar read FSpeed write SetSpeed;
    property Force: TForce read FForce write SetForce;
    property Target: TPoint read FTarget write SetTarget;
    procedure Move(X, Y: Integer);
    function MouseOver(): Boolean;
  end;

type
  TACreature =  array of TCreature;

type
  TCreatures = class(TObject)
  private
    pLifebar: ITexture;
    pSelEnemyUnit: ITexture;
    pSelAllyUnit: ITexture;
    pSelUserUnit: ITexture;
    FCreature: TACreature;
    procedure SetCreature(const Value: TACreature);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Add(X, Y, ID: Integer; Force: TForce);
    procedure ModLife(ID, Dam: Integer);
    function EmptyCell(X, Y: Integer): Boolean;
    property Creature: TACreature read FCreature write SetCreature;
    function GetCreatureID(X, Y: Integer): Integer;
    function GetTagret(C: Integer): Integer;
    procedure SetTargets;
    function Count: Integer;
    procedure Process(I: Integer);
    procedure Render();
    procedure Update();
  end;

implementation

uses SysUtils, Tile, uBox, uPF;

function GetDist(x1, y1, x2, y2: single): word;
begin
  Result := Round(sqrt(sqr(x2 - x1) + sqr(y2 - y1)));
end;

{ TCreature }

constructor TCreature.Create(X, Y, ID: Integer; Force: TForce);
begin
  inherited Create(X, Y, ID, 'Resources\Sprites\Creatures\' + CreatureFName[ID], pResMan);
  Self.Target := Point(0, 0);  
  Self.Active := True;
  Self.Force := Force;
  Life := TBar.Create;
  Life.SetMax(100);
  Life.SetToMax;
  Speed := TBar.Create;
  Speed.SetMax(100);
  Speed.SetToMax;
end;

destructor TCreature.Destroy;
begin
  Speed.Free;
  Life.Free;
  inherited;
end;

function TCreature.MouseOver: Boolean;
begin
  Result := (Round(ScreenPos.X) = Pos.X - MAP_LEFT) and (Round(ScreenPos.Y) = Pos.Y - MAP_TOP)
end;

procedure TCreature.Move(X, Y: Integer);
begin
  SetPosition(Pos.X + X, Pos.Y + Y);
end;

procedure TCreature.SetActive(const Value: Boolean);
begin
  FActive := Value;
end;

procedure TCreature.SetForce(const Value: TForce);
begin
  FForce := Value;
end;

procedure TCreature.SetLife(const Value: TBar);
begin
  FLife := Value;
end;

procedure TCreature.SetSpeed(const Value: TBar);
begin
  FSpeed := Value;
end;

procedure TCreature.SetTarget(const Value: TPoint);
begin
  FTarget := Value;
end;

{ TCreatures }

procedure TCreatures.Process(I: Integer);
var
  J, E: Integer;  
var
  LX, LY, NX, NY, JX, JY: Integer;

  function FreeCell(AX, AY: Integer): Boolean; stdcall;
  begin
    Result := True or EmptyCell(AX, AY);
  end;

{    if (Length(Enemy) > 0) then
    for J := 0 to PC.AP.Max do
      for I := 0 to High(Enemy) do
        if not Enemy[I].Life.IsMin
          and not PC.Life.IsMin then
        begin
          with Enemy[I] do
          if AP.IsMin then
          begin
            Process;
            AP.SetToMax;
          end else AP.Dec;
        end;     }

begin
  SetTargets;

  if (I = CURRENT_ALLY) and Creature[CURRENT_ALLY].Speed.IsMin then
  begin
    for J := 0 to Count - 1 do
      if Creature[J].Active then
        Creature[J].Speed.SetToMax;
  end;

//  for I := 0 to Count - 1 do
    if Creature[I].Active then
    begin
      if not DoPF(MAP_WIDTH, MAP_HEIGHT, Creature[I].Pos.X, Creature[I].Pos.Y,
        Creature[I].Target.X, Creature[I].Target.Y, @FreeCell, NX, NY) then Exit;//Continue;
      if (NX <= 0) or (NY <= 0) then Exit;//Continue;
      if (GetDist(Creature[I].Target.X, Creature[I].Target.Y, Creature[I].Pos.X, Creature[I].Pos.Y) > 9) then Exit;//Continue;
      if (Creature[I].Speed.IsMin) then Exit;//Continue;

      if not EmptyCell(NX, NY) then
      begin
        E := GetCreatureID(NX, NY);
        if (E > -1) and (Creature[I].Force <> Creature[E].Force) then
        begin
          Self.ModLife(E, -Rand(5, 15));
          Creature[I].Speed.SetToMin;
        end;
        Exit;//Continue;
      end;
      Creature[I].SetPosition(NX, NY);
      Creature[I].Speed.SetToMin;
    end;
end;

procedure TCreatures.Add(X, Y, ID: Integer; Force: TForce);
var
  I: Integer;
begin
  if (Count <> 0) then
  for I := 0 to Count - 1 do
    if not Creature[I].Active then
    begin
      Creature[I].Create(X, Y, ID, Force);
      Exit;
    end;
  SetLength(FCreature, Count + 1);
  Creature[Count - 1] := TCreature.Create(X, Y, ID, Force);
end;

function TCreatures.Count: Integer;
begin
  Result := System.Length(Creature);
end;

constructor TCreatures.Create;
begin
  pResMan.Load('Resources\Sprites\Interface\Lifebar.png', IEngineBaseObject(pLifebar), TEXTURE_LOAD_DEFAULT_2D);
  pResMan.Load('Resources\Sprites\Interface\SelEnemyUnit.png', IEngineBaseObject(pSelEnemyUnit), TEXTURE_LOAD_DEFAULT_2D);
  pResMan.Load('Resources\Sprites\Interface\SelAllyUnit.png', IEngineBaseObject(pSelAllyUnit), TEXTURE_LOAD_DEFAULT_2D);
  pResMan.Load('Resources\Sprites\Interface\SelUserUnit.png', IEngineBaseObject(pSelUserUnit), TEXTURE_LOAD_DEFAULT_2D);
end;

destructor TCreatures.Destroy;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do Creature[I].Free;
  pSelEnemyUnit := nil;
  pSelAllyUnit := nil;
  pSelUserUnit := nil;
  pLifebar := nil;
  inherited;
end;

procedure TCreatures.Render;
var
  I: Integer;
  W: Byte;

  procedure RenderSelUnit(T: ITexture);
  begin
    pRender2D.DrawTexture(T,
      Point2((Creature[I].Pos.X - MAP_LEFT) * TILE_SIZE + SCR_LEFT,
      (Creature[I].Pos.Y - MAP_TOP) * TILE_SIZE + SCR_TOP),
      Point2(TILE_SIZE, TILE_SIZE));
  end;

begin
  for I := 0 to Count - 1 do
    if Creature[I].Active then
    begin
      if (I <> CURRENT_ALLY) and Creature[I].MouseOver then
      case Creature[I].Force of
        fcEnemy: RenderSelUnit(pSelEnemyUnit);
        fcAlly: RenderSelUnit(pSelAllyUnit);
      end;
      if (I = CURRENT_ALLY) then RenderSelUnit(pSelUserUnit);

      pRender2D.DrawTexture(Creature[I].Texture,
        Point2((Creature[I].Pos.X - MAP_LEFT) * TILE_SIZE + SCR_LEFT,
        (Creature[I].Pos.Y - MAP_TOP) * TILE_SIZE + SCR_TOP),
        Point2(TILE_SIZE, TILE_SIZE));

      W := 30 - BarWidth(Creature[I].Life.Cur, Creature[I].Life.Max, 30);
      pRender2D.DrawTexture(pLifebar,
        Point2((Creature[I].Pos.X - MAP_LEFT) * TILE_SIZE + SCR_LEFT + 2 + W,
        (Creature[I].Pos.Y - MAP_TOP) * TILE_SIZE + SCR_TOP - 2),
        Point2(TILE_SIZE - 4 - (W * 2), 4));

      W := 30 - BarWidth(Creature[I].Speed.Cur, Creature[I].Speed.Max, 30);
      pRender2D.DrawTexture(pLifebar,
        Point2((Creature[I].Pos.X - MAP_LEFT) * TILE_SIZE + SCR_LEFT + 2 + W,
        (Creature[I].Pos.Y - MAP_TOP) * TILE_SIZE + SCR_TOP - 2 + (TILE_SIZE - 2)),
        Point2(TILE_SIZE - 4 - (W * 2), 4));

    {  TextOut((Creature[I].Pos.X - MAP_LEFT) * TILE_SIZE + SCR_LEFT + 2 + W,
        (Creature[I].Pos.Y - MAP_TOP) * TILE_SIZE + SCR_TOP - 2,
        StrToPAChar(IntToStr(I)
        + '|' + IntToStr(Creature[I].Pos.X) + ':' + IntToStr(Creature[I].Pos.Y)
        + '|' + IntToStr(Creature[I].Target.X) + ':' + IntToStr(Creature[I].Target.Y)
        ),
        Color4);  }
    end;
  Digit.Render;
end;

procedure TCreatures.ModLife(ID, Dam: Integer);
begin
  if (Dam = 0) or not Creature[ID].Active then Exit;
  Digit.Add(Creature[ID].Pos.X, Creature[ID].Pos.Y, Dam);
  if (Dam < 0) then Creature[ID].Life.Dec(Abs(Dam));
  if (Dam > 0) then Creature[ID].Life.Inc(Dam);
  if Creature[ID].Life.IsMin then Creature[ID].Active := False;
end;

procedure TCreatures.Update;
begin
  Digit.Update;
end;

procedure TCreatures.SetCreature(const Value: TACreature);
begin
  FCreature := Value;
end;

procedure TCreatures.SetTargets;
var
  I, T: Integer;
begin
  for I := 0 to Count - 1 do
    if Creature[I].Active and (I <> CURRENT_ALLY) then
    begin
      T := GetTagret(I);
      if (T > -1) then Creature[I].Target := Creature[T].Pos;
    end;
end;

function TCreatures.EmptyCell(X, Y: Integer): Boolean;
var
  I: Integer;
begin
  Result := True;
  for I := 0 to Count - 1 do
    if Creature[I].Active
    and (X = Creature[I].Pos.X)
    and (Y = Creature[I].Pos.Y) then
    begin
      Result := False;
      Exit;
    end;
end;

function TCreatures.GetCreatureID(X, Y: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
    if Creature[I].Active
    and (X = Creature[I].Pos.X)
    and (Y = Creature[I].Pos.Y) then
    begin
      Result := I;
      Exit;
    end;
end;

function TCreatures.GetTagret(C: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
    if Creature[I].Active and (I <> C) then
    begin
      if (GetDist(Creature[C].Pos.X, Creature[C].Pos.Y, Creature[I].Pos.X, Creature[I].Pos.Y) <= 5) then
      begin
        if (Creature[C].Force = Creature[I].Force) then Continue;
        Result := I;
        Exit;
      end;
    end;
end;

end.
